package com.blog.blog.util.aop;

import com.blog.blog.util.annotations.OpLog;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

@Aspect
@Component
public class OpLogAspect {

    private static final Logger log = LoggerFactory.getLogger(OpLogAspect.class);

    private final com.blog.blog.mapper.OperationLogMapper operationLogMapper;
    private final com.blog.blog.service.AuthService authService;
    private final com.blog.blog.mapper.UserMapper userMapper;

    public OpLogAspect(com.blog.blog.mapper.OperationLogMapper operationLogMapper,
                       com.blog.blog.service.AuthService authService,
                       com.blog.blog.mapper.UserMapper userMapper) {
        this.operationLogMapper = operationLogMapper;
        this.authService = authService;
        this.userMapper = userMapper;
    }

    @Around(
        "@annotation(com.blog.blog.util.annotations.OpLog) || " +
        "@within(com.blog.blog.util.annotations.OpLog) || " +
        "within(@org.springframework.web.bind.annotation.RestController *) || " +
        "@annotation(org.springframework.web.bind.annotation.RequestMapping) || " +
        "@annotation(org.springframework.web.bind.annotation.GetMapping) || " +
        "@annotation(org.springframework.web.bind.annotation.PostMapping) || " +
        "@annotation(org.springframework.web.bind.annotation.PutMapping) || " +
        "@annotation(org.springframework.web.bind.annotation.DeleteMapping)"
    )
    public Object around(ProceedingJoinPoint pjp) throws Throwable {
        long start = System.currentTimeMillis();
        Throwable error = null;
        Object result = null;
        try {
            result = pjp.proceed();
            return result;
        } catch (Throwable t) {
            error = t;
            throw t;
        } finally {
            long cost = System.currentTimeMillis() - start;
            try {
                record(pjp, result, error, cost);
            } catch (Exception ignored) { }
        }
    }

    private void record(ProceedingJoinPoint pjp, Object result, Throwable error, long cost) {
        MethodSignature ms = (MethodSignature) pjp.getSignature();
        OpLog ann = ms.getMethod().getAnnotation(OpLog.class);
        if (ann == null) ann = pjp.getTarget().getClass().getAnnotation(OpLog.class);

        jakarta.servlet.http.HttpServletRequest req = ((org.springframework.web.context.request.ServletRequestAttributes)
                org.springframework.web.context.request.RequestContextHolder.getRequestAttributes()).getRequest();

        String auth = req.getHeader("Authorization");
        Long userId = null;
        String username = null;
        if (auth != null && auth.startsWith("Bearer ")) {
            userId = authService.verify(auth.substring(7));
            if (userId != null) {
                com.blog.blog.po.UserPO u = userMapper.selectById(userId);
                username = u == null ? null : u.getUsername();
            }
        }

        String params;
        try {
            if (ann != null && !ann.saveRequestParams()) params = null;
            else {
                Object[] args = pjp.getArgs();
                java.util.List<Object> safe = new java.util.ArrayList<>();
                for (Object a : args) {
                    if (a instanceof jakarta.servlet.http.HttpServletRequest || a instanceof jakarta.servlet.http.HttpServletResponse || a instanceof org.springframework.web.multipart.MultipartFile) continue;
                    safe.add(a);
                }
                params = new com.fasterxml.jackson.databind.ObjectMapper().writeValueAsString(safe);
            }
        } catch (Exception e) {
            params = null;
        }

        String opName = (ann != null && !ann.value().isEmpty() ? ann.value() : ms.getDeclaringTypeName() + "." + ms.getName());
        if (ann != null && !ann.module().isEmpty()) {
            opName = ann.module() + ":" + opName;
        }

        String ua = req.getHeader("User-Agent");
        String ip = req.getHeader("X-Forwarded-For");
        if (ip == null || ip.isBlank()) ip = req.getHeader("X-Real-IP");
        if (ip == null || ip.isBlank()) ip = req.getRemoteAddr();
        String method = req.getMethod() + " " + req.getRequestURI();
        int status = error == null ? 1 : 0;
        String errMsg = error == null ? null : String.valueOf(error.getMessage());

        // 控制台日志
        log.info("oplog userId={} username={} op={} method={} status={} cost={}ms", userId, username, opName, method, status, cost);

        // 入库
        operationLogMapper.insertLog(userId, username, opName, method, params, ip, ua, status, errMsg, cost);
    }
}


